<?php

require_once('inc/constants.php');
require_once('inc/out.php');

class WSB_Frontend
{

    /**
     * Singleton instance
     *
     * @var WSB_Frontend
     */
    private static $_instance = null;

    /**
     * Die Seite
     *
     * @var Cms_Model_Page
     */
    private $_page;

    /**
     * Das Layout
     *
     * @var Zend_Layout
     */
    private $_layout;

    /**
     * Das View Objekt
     *
     * @var Zend_View
     */
    private $_view;

    /**
     * @var integer
     */
    private $_languageId = 0;

    /**
     * Singleton: No public access to constructor.
     *
     */
    private function __construct()
    {}

    /**
     * Singleton instance
     *
     * @return WSB_Frontend
     */
    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    /**
     * @return void
     */
	public function run()
	{

		// Es gibt zur Zeit Probleme mit magic_quotes_gpc=On
		if ((function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc())
		   || (ini_get('magic_quotes_sybase') && (strtolower(ini_get('magic_quotes_sybase'))!="off")) )
		{
			self::_stripslashes($_GET);
			self::_stripslashes($_POST);
			self::_stripslashes($_COOKIE);
			self::_stripslashes($_REQUEST);
		}

		$configurations = require(WSB_APP_PATH . 'inc/config.php');

		// Include Pfade setzen
		$paths = array();
		if(!empty($configurations['includePaths']['zend'])) {
			$paths[] = $configurations['includePaths']['zend'];
		}
		$paths[] = WSB_PATH;
		$paths[] = WSB_APP_PATH . 'library';
		$paths[] = WSB_APP_PATH . 'library/PEAR';
		set_include_path(implode(PATH_SEPARATOR, $paths));

		require_once('Zend/Loader/Autoloader.php');
		$autoloader = Zend_Loader_Autoloader::getInstance();
		$autoloader->setFallbackAutoloader(true);

		// Allgemein benötigte Klassen inkludieren
		// @todo: Zend_Loader_Autoloader verwenden
		require_once(WSB_MODULES_PATH . 'member/models/DbTable/Beuser.php');
		require_once(WSB_MODULES_PATH . 'member/models/DbTable/Feuser.php');
		require_once(WSB_MODULES_PATH . 'cms/models/DbTable/Template.php');
		require_once(WSB_MODULES_PATH . 'cms/models/DbTable/Language.php');
		require_once(WSB_MODULES_PATH . 'cms/models/DbTable/Placeholder.php');
		require_once(WSB_MODULES_PATH . 'cms/models/DbTable/Xtension.php');
		require_once(WSB_MODULES_PATH . 'cms/models/XtensionDescriptor.php');
		require_once(WSB_MODULES_PATH . 'cms/models/DbTable/PageLanguage.php');
		require_once(WSB_MODULES_PATH . 'cms/models/DbTable/Page.php');
		require_once(WSB_MODULES_PATH . 'cms/models/DbTable/Container.php');
		require_once(WSB_MODULES_PATH . 'cms/Url.php');


		// Klassendatei Cache
		$classFileIncCache = WSB_APP_PATH.  'tmp/cache/frontend/pluginLoaderCache.php';
		if (file_exists($classFileIncCache)) {
			include_once $classFileIncCache;
		}
		Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);

		$timer = new Benchmark_Timer();
		$timer->start();

		// Hooks
        $hooks = WSB_Hooks::instance();
        if(!empty($configurations['hooks'])) {
            $hooks->addHooks($configurations['hooks']);
        }

		// Configuration
		$config = new Zend_Config($configurations);
		Zend_Registry::set('Zend_Config', $config);

		$timer->setMarker('Zend_Config');

		// Header UTF-8
		header('Content-Type: text/html; charset=utf-8');

		// Initialisiere Session Handling
		$sessionSavePath = WSB_APP_PATH . 'tmp/session';
		if(!is_dir($sessionSavePath)) {
			die(sprintf("Error: Missing folder '%s'.", $sessionSavePath));
		}
		if(!is_writeable($sessionSavePath)) {
			die(sprintf("Error: Folder '%s' is not writable.", $sessionSavePath));
		}
		Zend_Session::setOptions(array('save_path' => $sessionSavePath));
		Zend_Session::start();

		$timer->setMarker('Zend_Session');

		// DB
		$db = Zend_Db::factory($config->database);
		Zend_Db_Table_Abstract::setDefaultAdapter($db);
		if($config->application->profiler) {
			$db->getProfiler()->setEnabled(true);
		}
		// Using a Default Metadata Cache for all Table Objects
		if($config->database->useMetadataCache) {
			$frontendOptions = array('automatic_serialization' => true);
			$backendOptions  = array('cache_dir' => WSB_APP_PATH . 'tmp/cache');
			$cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
			Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
		}

		$timer->setMarker('Zend_Db');

		// Login
		if(isset($_POST['action']) && ($_POST['action'] == 'login')) {
			$username = isset($_POST['username']) ? $_POST['username'] : '';
			$password = isset($_POST['password']) ? $_POST['password'] : '';
			$loggedIn = $this->loginFeuser($username, $password);
			if($loggedIn && !empty($_POST['redirect'])) {
				header('Location: ' . $_POST['redirect']);
				exit;
			}
		}
		$timer->setMarker('Login');

		// Logout
		if(isset($_REQUEST['action']) && ($_REQUEST['action'] == 'logout')) {
			$this->logoutFeuser();
		}

		$timer->setMarker('Logout');

		// Caching (muss zwingend nach Login/Logout platziert sein)
		$frontendOptions = array(
			'lifetime' => 7200,
			'debug_header' => true, // fuer das Debuggen
			'default_options' => array(
				'cache' => false,
				'cache_with_get_variables' => true,
				'cache_with_cookie_variables' => true,
				'cache_with_session_variables' => false,
				'make_id_with_cookie_variables' => false
		   )
		);
		$backendOptions = array(
		    'cache_dir' => WSB_APP_PATH . 'tmp/cache/frontend/'
		);
		$cache = Zend_Cache::factory('Page', 'File', $frontendOptions, $backendOptions);

		// Wenn der Cache gefunden wurde, wird das Ergebnis zum Browser geschickt, und das Skript stoppt hier
		$cache->start();

		$timer->setMarker('Zend_Cache');

		// Page ID
		$requestedPageId = $this->_getRequestedPageId();
		if(empty($requestedPageId)) {
			die('Error: No page id found.');
		}

		$timer->setMarker('Get Requested Page ID');

		// Page
		$page = $this->_getPage($requestedPageId);

		// Error-404 Page not found
		$notFoundPage = null;
		if(!$page instanceof Cms_Model_Page) {
			$notFoundPage = $this->_getNotFoundPage();
		} elseif(($page->hidden == 1) || ($page->deleted == 1)) {
			$notFoundPage = $this->_getNotFoundPage();
		}
		if($notFoundPage instanceof Cms_Model_Page) {
			$page = $notFoundPage;
		}

		$timer->setMarker('Get page object');

		// Zugriff
		$feUser = WSB_Auth_Frontend::getInstance()->getIdentity();
		// Keine Pruefung bei "Page not found" Seiten!!
		if(is_null($notFoundPage) && !$page->hasAccess($feUser)) {
			$accessDeniedPage = $this->_getAccessDeniedPage();
			if($accessDeniedPage instanceof Cms_Model_Page) {
				$page = $accessDeniedPage;
			}
		}

		$this->_setPage($page);
		$timer->setMarker('Test access');

		// Template
		$template = $page->getTemplate();
		if(!$template instanceof Cms_Model_Template) {
			die("Error: No template defined.");
		}

		$timer->setMarker('Get template object');

		// Language
		$language = $this->_getLanguage($page);
		if(!$language instanceof Cms_Model_Language) {
			die("Error: No language defined.");
		}

		$timer->setMarker('Get language object');

		// Locale
		$locale = new Zend_Locale($language->iso);
		Zend_Registry::set('Zend_Locale', $locale);

		$timer->setMarker('Get locale object');

		// Translate
		$translate = new Zend_Translate('csv', WSB_APP_PATH . 'i18n', $locale, array('disableNotices' => true, 'scan' => Zend_Translate::LOCALE_FILENAME));
		Zend_Registry::set('Zend_Translate', $translate);

		$timer->setMarker('Translate object');

		// Layout
		$this->_layout = new Zend_Layout();
		$this->_layout->setLayoutPath(WSB_PATH . $config->frontend->layout);
		$this->_layout->pageId = $page->id;

		$timer->setMarker('Zend_Layout');

		// View
		$this->_view = new WSB_Xtension_View();
		//$this->_view->setEncoding('utf-8');
		$this->_view->doctype('XHTML1_TRANSITIONAL');
		$this->_view->addHelperPath(WSB_APP_PATH . 'library/WSB/View/Helper/', 'WSB_View_Helper');
		$this->_view->addBasePath(WSB_PATH . $config->frontend->layout . '/views');

		// META-Daten
		// @todo: Dies muss noch flexibler geloest werden.
		if(!empty($config->meta->keywords)) {
			$this->_view->headMeta()->appendName('keywords', $config->meta->keywords);
		}
		if(!empty($config->meta->description)) {
			$this->_view->headMeta()->appendName('description', $config->meta->description);
		}
		$this->_view->headMeta()->appendHttpEquiv('content-type', 'text/html; charset=utf-8');

		$timer->setMarker('Zend_View');

		// Mail
		$this->_initMail();

		$pageRenderer = new WSB_PageRenderer($page, $template, $language);
		$pageRenderer->render($this->_view);

		$this->_layout->setView($this->_view);
		echo $this->_layout->render();

		if($config->application->profiler) {
			$this->_renderProfilerInfos();
		}

		if($config->benchmark) {
			$timer->setMarker('PageRenderer');
			$timer->stop();
			$timer->display();
		}

	}

	/**
	 * @param mixed $value
	 * @return mixed
	 */
	private function _stripslashes(&$value)
	{
	    $value = is_array($value) ?
	                array_map(array('WSB_Frontend', '_stripslashes'), $value) :
	                stripslashes($value);
	    return $value;
	}

	/**
	 * @return Zend_Layout
	 */
	public function getLayout()
	{
		return $this->_layout;
	}

	/**
	 * @return Zend_View
	 */
	public function getView()
	{
		return $this->_view;
	}

	/**
	 * @return void
	 */
	private function _renderProfilerInfos()
	{
		$profiler = Zend_Db_Table_Abstract::getDefaultAdapter()->getProfiler();
		$totalTime    = $profiler->getTotalElapsedSecs();
		$queryCount   = $profiler->getTotalNumQueries();
		$longestTime  = 0;
		$longestQuery = null;
		echo 'Executed ' . $queryCount . ' queries in ' . $totalTime . ' seconds' . "<br />\n";
		echo 'Average query length: ' . $totalTime / $queryCount . ' seconds' . "<br />\n";
		echo 'Queries per second: ' . $queryCount / $totalTime . "<br />\n";
		foreach ($profiler->getQueryProfiles() as $query) {
		    echo $query->getQuery();
		    echo "<br />";
			echo $query->getElapsedSecs();
		    echo "<br />";
		    if ($query->getElapsedSecs() > $longestTime) {
		        $longestTime  = $query->getElapsedSecs();
		        $longestQuery = $query->getQuery();
		    }
		    echo "<hr />";
		}
		echo 'Longest query length: ' . $longestTime . "<br />\n";
		echo "Longest query: \n" . $longestQuery . "<br />\n";
	}

	/**
	 * @param string $username
	 * @param string $password
	 * @return boolean
	 */
	public function loginFeuser($username, $password)
	{
		if(!empty($username) && !empty($password)) {
			$adapter = new WSB_Auth_Adapter_Frontend($username, md5($password));
			$auth = WSB_Auth_Frontend::getInstance();
			$result = $auth->authenticate($adapter);
			if($result->isValid()) {
				$feuser = $auth->getIdentity();
				WSB_Log::info(sprintf('Website user "%s" logged in (id=%d).', $username, $feuser->id));
				return true;
			} else {
				WSB_Log::alert(sprintf('Website user "%s" tried to log in with password "%s".', $username, $password));
			}
		}
		return false;
	}

	/**
	 * @return boolean
	 */
	public function logoutFeuser()
	{
		$auth = WSB_Auth_Frontend::getInstance();
		$feuser = $auth->getIdentity();
		$auth->clearIdentity();
		if($feuser instanceof Member_Model_DbTable_Feuser_Row) {
			WSB_Log::info(sprintf('Website user "%s" logged out (id=%d).', $feuser->username, $feuser->id));
		}
	}

	/**
	 * @param Cms_Model_Page $page
	 */
	private function _setPage(Cms_Model_Page $page)
	{
		$this->_page = $page;
	}

	/**
	 * Seite fuer "Page not found" zurueckgeben.
	 * Falls keine gueltige Seite ausgelesen kann, wird das Script mit einer Fehlermeldung abgebrochen.
	 *
	 * @return Cms_Model_Page
	 */
	private function _getNotFoundPage()
	{
		header("HTTP/1.0 404 Not Found");
		$config = Zend_Registry::get('Zend_Config');
		$notFoundPageId = intval($config->application->notFoundPageId);
		if(empty($notFoundPageId)) {
			die('Error: Missing setting for \'notFoundPageId\' in config.php');
		}
		$notFoundPage = $this->_getPage($notFoundPageId);
		if($notFoundPage instanceof Cms_Model_Page) {
			return $notFoundPage;
		}
		die('Error 404 - Page not found');
	}

	/**
	 * Seite fuer "Access Denied" zurueckgeben.
	 * Falls keine gueltige Seite ausgelesen kann, wird das Script mit einer Fehlermeldung abgebrochen.
	 *
	 * @return Cms_Model_Page
	 */
	private function _getAccessDeniedPage()
	{
		$config = Zend_Registry::get('Zend_Config');
		$accessDeniedPageId = intval($config->application->accessDeniedPageId);
		if(empty($accessDeniedPageId)) {
			return $this->_getNotFoundPage();
		} else {
			$_GET['redirect'] = $_SERVER['REQUEST_URI'];
			$accessDeniedPage = $this->_getPage($accessDeniedPageId);
			if($accessDeniedPage instanceof Cms_Model_Page) {
				return $accessDeniedPage;
			}
		}
		die('Error - Access denied');
	}

	/**
	 * @return integer
	 */
	private function _getRequestedPageId()
	{
		if(isset($_SERVER['REDIRECT_URL'])) {
			$relUrlToWebsite = self::addTrailingSlash(dirname($_SERVER['SCRIPT_NAME']));
			$relUrlToWebsite = preg_replace('#^' . $relUrlToWebsite . '#', '', $_SERVER['REDIRECT_URL']);
			$relUrlToWebsite = preg_replace('#.html$#', '', $relUrlToWebsite);
			// Only the domain like www.test.com
			// There's no need to translate the url
			if(empty($relUrlToWebsite)) {
				return 0;
			}
			$urlSegments = explode('/', $relUrlToWebsite);

			// add the website roots path segment
			$pageTree = WSB_Node_Page::getTree();
			array_unshift($urlSegments, $pageTree->getUrlPathSegment());

			return self::getPageIDFromRealUrl($pageTree, $urlSegments);
		} else {
			$request = new Zend_Controller_Request_Http();
			$id = intval($request->getQuery('id'));
			if(empty($id)) {
				$id = WSB_Node_Page::getTree()->getPageId();
			}
			return intval($id);
		}
	}

	/**
	 * Gibt die "kalkulierte" Page ID zurueck.
	 *
	 * @return integer
	 */
	public function getPageId()
	{
		if($this->_page instanceof Cms_Model_Page) {
			return intval($this->_page->id);
		}
		return 0;
	}

	/**
	 * Gibt die "kalkulierte" Seite zurueck.
	 *
	 * @param integer $id
	 * @return Cms_Model_Page
	 */
	private function _getPage($id)
	{
		return WSB_Node_Page::getTree()->getPageForFrontend(intval($id));
	}

	/**
	 * Sprache abhaengig von einem allfaelligen L-Parameter oder Defaultsprache
	 *
	 * @param Cms_Model_Page $page
	 * @return Cms_Model_Language
	 */
	private function _getLanguage(Cms_Model_Page $page)
	{
		$languages = $page->getLanguages();
		if(is_array($languages) && count($languages) > 0) {
			$request = new Zend_Controller_Request_Http();
			$L = intval($request->getQuery('L'));
			foreach($languages AS $language) {
				// Nimm die per L-Var aufgerufene Sprache
				if($language->id == $L) {
					$this->_languageId = $language->id;
					return $language;
				}
			}
			// Nimm die erste Sprache
			$language = reset($languages);
			$this->_languageId = $language->id;
			return $language;
		}
		return null;
	}

	/**
	 * @return integer
	 */
	public function getLanguageId()
	{
		return $this->_languageId;
	}

	/**
	 * Translate a speaking url to the page id
	 *
	 * @return int -1 means page not found, 0 means no translation
	 */
	public static function translateRealUrl()
	{}

	/**
	 * Remove a trailing slash from a path
	 *
	 * @param string $path
	 * @return string
	 */
	public static function removeTrailingSlash($path)
	{
		return preg_replace('#\/$#', '', $path);
	}

	/**
	 * Add a trailing slash to a path
	 *
	 * @param string $path
	 * @return string
	 */
	public static function addTrailingSlash($path)
	{
		return self::removeTrailingSlash($path) . '/';
	}

	/**
	 * Get the page id for a given speaking url
	 * This is a RECURSIVE method !
	 *
	 * @param Node $page
	 * @param array $segments
	 * @return integeer
	 */
	private static function getPageIDFromRealUrl(WSB_Node_Page $page, $segments)
	{
		$level = $page->getLevel();
		if(isset($segments[$level])) {
			if($page->getUrlPathSegment() == $segments[$level]) {
				if(count($segments) == ($level+1)) {
					return $page->getId();
				}
				foreach($page->getChildren() AS $child) {
					$result = self::getPageIDFromRealUrl($child, $segments);
					if($result > 0) {
						return $result;
					}
				}
			}
		}
		return -1;
	}

	private function _initMail()
	{
		$config = Zend_Registry::get('Zend_Config');
		if(isset($config->mail->transport) && ($config->mail->transport == 'smtp') && isset($config->mail->smtp)) {
			$smtpConfig = array(
				'auth' => 'login',
			    'username' => $config->mail->smtp->username,
			    'password' => $config->mail->smtp->password
			);
			$transport = new Zend_Mail_Transport_Smtp($config->mail->smtp->server, $smtpConfig);
			Zend_Mail::setDefaultTransport($transport);
		}
	}

}
